package com.iscreen.utils.zip;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.iscreen.utils.exceptions.IScreenException;
import com.iscreen.utils.exceptions.IScreenExceptionCodes;

public class ZipUtils
{
	private final static int BUFFER_SIZE = 4 * 1024;

	public static List<String> extract(String zipFileName, String destinationFolderName)
			throws IScreenException
	{
		ZipFile zipFile = getZipFile(zipFileName);
		File destinationFolder = getDestinationFolder(destinationFolderName);

		return extract(zipFile, destinationFolder);
	}

	private static List<String> extract(ZipFile zipFile, File destinationFolder)
			throws IScreenException
	{
		Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();

		List<String> destinationFileNames = new ArrayList<String>();
		while (zipEntries.hasMoreElements())
		{
			String destinationFileName = extractEntry(zipFile, zipEntries.nextElement(), destinationFolder);
			if (destinationFileName != null)
			{
				destinationFileNames.add(destinationFileName);
			}
		}
		return destinationFileNames;
	}

	private static String extractEntry(ZipFile zipFile, ZipEntry zipEntry, File destinationFolder) throws IScreenException
	{
		if (zipEntry.isDirectory())
		{
			extractFolder(zipEntry, destinationFolder);
			return null;
		}
		else
		{
			return extractFile(zipFile, zipEntry, destinationFolder);
		}
	}

	// TODO This is a duplicate of FileDownloader.copy
	private static void copy(InputStream is, OutputStream os)
			throws IScreenException
	{
		BufferedInputStream input = new BufferedInputStream(is);
		BufferedOutputStream output = new BufferedOutputStream(os);

		try
		{
			byte[] buffer = new byte[BUFFER_SIZE];

			int bytesRead;
			while ((bytesRead = input.read(buffer)) != -1)
			{
				output.write(buffer, 0, bytesRead);
			}
		}
		catch (IOException e)
		{
			throw new IScreenException(IScreenExceptionCodes.ZIP_EXTRACT_ERROR,
					e);
		}
		finally
		{
			if (input != null)
			{
				try
				{
					input.close();
				}
				catch (IOException e)
				{
					// TODO only log is needed here
					e.printStackTrace();
				}
			}
			if (output != null)
			{
				try
				{
					output.close();
				}
				catch (IOException e)
				{
					// TODO only log is needed here
					e.printStackTrace();
				}
			}
		}

	}

	private static String extractFile(ZipFile zipFile, ZipEntry zipEntry,
			File destinationFolder) throws IScreenException
	{
		InputStream zipEntryInputStream = getZipEntryInputStream(zipFile,
				zipEntry);

		File destinationEntryFile = new File(destinationFolder, zipEntry.getName());

		OutputStream destinationEntryOutputStream = getDestinationEntryOutputStream(destinationEntryFile);

		copy(zipEntryInputStream, destinationEntryOutputStream);
		
		return destinationEntryFile.getAbsolutePath();
	}

	private static OutputStream getDestinationEntryOutputStream(File destinationEntryFile) throws IScreenException
	{
		try
		{
			return new BufferedOutputStream(new FileOutputStream(
					destinationEntryFile));
		}
		catch (FileNotFoundException e)
		{
			throw new IScreenException(IScreenExceptionCodes.ZIP_EXTRACT_ERROR,
					e);
		}
	}

	private static InputStream getZipEntryInputStream(ZipFile zipFile,
			ZipEntry zipEntry) throws IScreenException
	{
		try
		{
			return zipFile.getInputStream(zipEntry);
		}
		catch (IOException e)
		{
			throw new IScreenException(IScreenExceptionCodes.ZIP_EXTRACT_ERROR,
					e);
		}
	}

	private static void extractFolder(ZipEntry zipEntry, File destinationFolder)
	{
		File dir = new File(destinationFolder, zipEntry.getName());
		dir.mkdirs();
	}

	private static File getDestinationFolder(String destinationFolderName)
			throws IScreenException
	{
		File destinationFolder = new File(destinationFolderName);
		createFolder(destinationFolder);
		return destinationFolder;
	}

	private static void createFolder(File folder) throws IScreenException
	{
		folder.mkdirs();
		if (!folder.exists())
		{
			throw new IScreenException(IScreenExceptionCodes.ZIP_EXTRACT_ERROR,
					"Unable to create destination folder "
							+ folder.getAbsolutePath() + " for zip extraction.");
		}
	}

	private static ZipFile getZipFile(String zipFileName)
			throws IScreenException
	{
		try
		{
			ZipFile zipFile = new ZipFile(zipFileName);
			return zipFile;
		}
		catch (IOException e)
		{
			throw new IScreenException(IScreenExceptionCodes.ZIP_EXTRACT_ERROR,
					e);
		}
	}
}
